﻿//========================================================= 
/**@file odbc.h
 * @brief Zabbix ODBC监控模块
 * 
 * @date 2018-08-08   14:43:40
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_ODBC_H_
#define _LIBZHOUYB_ODBC_H_
//--------------------------------------------------------- 
#include "../odbc/odbc_extractor.h"
using zhou_yb::wrapper::odbc_extractor::sql_env;
using zhou_yb::wrapper::odbc_extractor::sql_dbc;
using zhou_yb::wrapper::odbc_extractor::sql_stmt;

#include "../rapidjson/rapidjson_extrator.h"
using zhou_yb::wrapper::rapidjson_extractor::RapidJsonArgParser;

#include "../../extension/ability/FolderHelper.h"
using zhou_yb::extension::ability::FileHelper;
//--------------------------------------------------------- 
namespace zhou_yb {
namespace wrapper {
namespace zabbix_extractor {
//--------------------------------------------------------- 
/**
 * @brief ODBC监控模块
 *
 * - 接口定义:
 *  - 支持使用SQL和Procedure的方式获取监控结果
 *  - 监控结果必须是数字类型
 *  - 监控返回值为负数时表示操作失败
 * .
 *
 * @date 2018-08-15 11:02
 */
class odbc_expander
{
public:
    typedef int value_type;

    /// 错误标识
    enum CodeErr
    {
        /// 操作成功
        kSuccess = 0,
        /// 操作错误时的错误码
        kError,
        /// 配置错
        kConfigError,
        /// 操作错误
        kOperateError,
        /// 有效性错误
        kNullError,
        /// 参数错误
        kArgError,
        /// 初始化错误
        kInitError
    };
    /// 返回并输出错误码
    static inline int put_err(CodeErr err = kError)
    {
        int errCode = static_cast<int>(err);
        return -(errCode);
    }
    /// 通过命令行的方式执行
    template<class TMonitor>
    static value_type execute(const char* cfg)
    {
        string sJson = FileHelper::ReadToEnd(cfg);
        if(sJson.length() < 1) return put_err(kConfigError);

        StringArguments arg;
        RapidJsonArgParser parser;
        parser.Parse(ByteArray(sJson.c_str(), sJson.length()), arg);

        value_type rlt = 0;
        CodeErr err = TMonitor::execute(arg, rlt);
        if(err == kSuccess)
            return rlt;
        return put_err(err);
    }
    /**
     * @brief 命令行参数调用
     * @date 2018-08-09 14:57
     * 
     * @param [in] monitor 监控项
     * @param [in] argc 命令行参数个数
     * @param [in] argv 命令行参数
     *
     * @warning 第一个参数为配置文件路径
     */
    template<class TMonitor>
    static value_type execute(int argc, const char* argv[])
    {
        if(argc < 2) return put_err(kError);
        return execute(argv[1]);
    }
    /**
     * @brief 连接ODBC
     * @date 2018-08-15 11:05
     *
     * @param [in] Server : string 服务端名称
     * @param [in] User : string 登陆账户
     * @param [in] Password : string 登陆密码
     */
    static sql_stmt connect(const IStringArguments& arg)
    {
        string svr = arg["Server"].To<string>();
        string usr = arg["User"].To<string>();
        string pwd = arg["Password"].To<string>();

        sql_env env = sql_env::create();
        sql_dbc dbc = env.connect(svr.c_str(), usr.c_str(), pwd.c_str());
        sql_stmt stmt = dbc.create();
        return stmt;
    }
};
//--------------------------------------------------------- 
/**
 * @brief 存储过程监控
 *
 * @description 通过调用存储过程获取监控数据,存储过程声明形如:void func(string arg, int& ret);
 *
 * @date 2018-08-09 14:36
 */
class odbc_procedure
{
public:
    /**
     * @brief ODBC调用存储过程返回 int 结果
     * @date 2018-08-14 16:36
     *
     * @param [in] Server : string 服务端名称
     * @param [in] User : string 登陆账户
     * @param [in] Password : string 登陆密码
     * @param [in] Direct : string 调用的存储过程名称
     * @param [in] Input : string [default:null] 调用存储过程时传入的参数
     *
     * @param rlt [out] 存储过程执行结果
     */
    static odbc_expander::CodeErr execute(const IStringArguments& arg, odbc_expander::value_type& rlt)
    {
        sql_stmt stmt = odbc_expander::connect(arg);
        if(!stmt.is_valid())
            return odbc_expander::kInitError;

        string direct = arg["Direct"].To<string>();
        string direct_arg = arg["Input"].To<string>("null");
        
        int ret = 0;
        SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, SQL_DESC_LENGTH, 
            0, ctype_cast(SQLPOINTER)direct_arg.c_str(), direct_arg.length(), NULL);
        SQLBindParameter(stmt, 2, SQL_PARAM_INPUT_OUTPUT, SQL_C_LONG, SQL_INTEGER, 
            0, 0, &ret, 0, NULL);
        string cmd = "{ call ";
        cmd += direct;
        cmd += "(?,?) }";
        if(!stmt.exec_direct(cmd.c_str()))
            return odbc_expander::kOperateError;
        rlt = ret;
        return odbc_expander::kSuccess;
    }
};
//--------------------------------------------------------- 
/**
 * @brief SQL语句执行监控
 * @date 2018-08-16 09:56
 */
class odbc_sql
{
public:
    /**
     * @brief SQL执行过程返回 int 结果
     * @date 2018-08-16 10:01
     *
     * @param [in] Server : string 服务端名称
     * @param [in] User : string 登陆账户
     * @param [in] Password : string 登陆密码
     * @param [in] SQL : string 需要执行的SQL语句
     *
     * @param rlt [out] SQL执行结果(int)
     */
    static odbc_expander::CodeErr execute(const IStringArguments& arg, odbc_expander::value_type& rlt)
    {
        sql_stmt stmt = odbc_expander::connect(arg);
        if(!stmt.is_valid())
            return odbc_expander::kInitError;

        string sql = arg["SQL"].To<string>();
        if(!stmt.prepare(sql.c_str()) || !stmt.execute())
            return odbc_expander::kOperateError;
        SQLINTEGER count = 0;
        if(!stmt.fetch() || !stmt.get_data(1, SQL_C_LONG, &count))
            return odbc_expander::kArgError;
        rlt =  static_cast<odbc_expander::value_type>(count);
        return odbc_expander::kSuccess;
    }
};
//--------------------------------------------------------- 
/// 将对应的ODBC模块封装为监控项
template<class TMonitor>
class odbc_monitor
{
public:
    typedef odbc_expander::value_type value_type;

    static value_type execute(int argc, const char* argv)
    {
        return odbc_expander::execute<TMonitor>(argc, argv);
    }
};
//--------------------------------------------------------- 
/// ODBC SQL监控项
typedef odbc_monitor<odbc_sql> sql_monitor;
/// ODBC 存储过程监控项
typedef odbc_monitor<odbc_procedure> procedure_monitor;
//--------------------------------------------------------- 
} // namespace zabbix_extractor 
} // namespace wrapper
} // namespace zhou_yb 
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_ODBC_H_
//========================================================= 